/* eslint-disable max-classes-per-file */

import Phaser from 'phaser'
import CONST from '../const'
import itemData from '../assets/tilemaps/item-data.json'

const _ = require('lodash')

let blockInput = false

/**
 * make the enemy sprite
 *
 * usage: in our scene main, do the following
 * let enemySprite = new EnemySprite({scene:this,x:game.config.width/2,y:game.config.height/2});
 */
class BaseSprite extends Phaser.GameObjects.Sprite {
  constructor (scene) {
    super(scene)
    scene.add.existing(this)

    this.setInteractive()
  }

  // animate walk but move nowhere
  animate (spriteSheetName, frameArray) {
    this.anims.create({
      key: 'animate',
      frames: this.anims.generateFrameNumbers(spriteSheetName, { frames: frameArray }),
      frameRate: 8,
      repeat: -1
    })
    this.play('animate')
  }
}

export class FloorUpSprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_FLOOR_UP, [0])
  }
}

export class FloorDownSprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_FLOOR_DOWN, [0])
  }
}

export class EnemySprite1 extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ENEMY, [0, 1, 2, 3])
  }
}

export class EnemySprite2 extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ENEMY, [4, 5, 6, 7])
  }
}

export class EnemySprite3 extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ENEMY, [8, 9, 10, 11])
  }
}

// 黄钥匙
export class YellowKeySprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ITEM, [16])
  }
}

// 蓝钥匙
export class BlueKeySprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ITEM, [17])
  }
}

// 红钥匙
export class RedKeySprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ITEM, [18])
  }
}

// 黄门
export class YellowDoorSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_DOOR, [0])

    this.anims.create({
      key: 'openDoor',
      frames: this.anims.generateFrameNumbers(CONST.SPRITE_SHEET_DOOR, { frames: [0, 4, 8, 12] }),
      frameRate: 32
    })

    // 动画完成后销毁
    this.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
      this.destroy()

      blockInput = false
    }, this)
  }
}

// 蓝门
export class BlueDoorSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_DOOR, [1])

    this.anims.create({
      key: 'openDoor',
      frames: this.anims.generateFrameNumbers(CONST.SPRITE_SHEET_DOOR, { frames: [1, 5, 9, 13] }),
      frameRate: 32
    })

    // 动画完成后销毁
    this.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
      this.destroy()

      blockInput = false
    }, this)
  }
}

// 红门
export class RedDoorSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_DOOR, [2])

    this.anims.create({
      key: 'openDoor',
      frames: this.anims.generateFrameNumbers(CONST.SPRITE_SHEET_DOOR, { frames: [2, 6, 10, 14] }),
      frameRate: 32
    })

    // 动画完成后销毁
    this.on(Phaser.Animations.Events.ANIMATION_COMPLETE, () => {
      this.destroy()

      blockInput = false
    }, this)
  }
}

// 小血瓶
export class SmallBottleSprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    super.animate(CONST.SPRITE_SHEET_ITEM, [4])
  }
}

// 大血瓶
export class BigBottleSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ITEM, [5])
  }
}

// 蓝宝石
export class BlueGemSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ITEM, [0])
  }
}

// 红宝石
export class RedGemSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ITEM, [1])
  }
}

// 铁盾
export class IronShieldSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ITEM, [56])
  }
}

// 骷髅人
export class SkeletonManSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [0, 1, 2, 3])
  }
}

// 骷髅士兵
export class SkeletonSoldierSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [4, 5, 6, 7])
  }
}

// 小蝙蝠
export class SmallBatSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [16, 17, 18, 19])
  }
}

// 绿史莱姆
export class GreenSlimeSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [32, 33, 34, 35])
  }
}

// 红史莱姆
export class RedSlimeSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [36, 37, 38, 39])
  }
}

// 黑史莱姆
export class BlackSlimeSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [40, 41, 42, 43])
  }
}

// 初级法师
export class PrimaryWizardSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [48, 49, 50, 51])
  }
}

// 兽面人
export class OrcFaceManSprite extends BaseSprite {
  constructor (scene) {
    super(scene)
    super.animate(CONST.SPRITE_SHEET_ENEMY, [64, 65, 66, 67])
  }
}

export class HeroSprite extends BaseSprite {
  constructor (scene) {
    super(scene)

    // init hero image
    super.animate(CONST.SPRITE_SHEET_HERO, [2])

    // listen to keyboard input
    this.listenKeyboardInput()
    // create walk animation
    this.createWalkAnimation()
  }

  listenKeyboardInput () {
    // 便捷地图跳转
    this.scene.input.keyboard.on('keydown', _.throttle((event) => {
      if (event.code === 'PageDown') {
        this.scene.goNextScene()
      } else if (event.code === 'PageUp') {
        this.scene.goPrevScene()
      }
    }, 200))

    this.scene.input.keyboard.on('keydown', (event) => {
      if (event.code === 'ArrowLeft') {
        this.handleKeyboardInput('LEFT')
      } else if (event.code === 'ArrowRight') {
        this.handleKeyboardInput('RIGHT')
      } else if (event.code === 'ArrowUp') {
        this.handleKeyboardInput('UP')
      } else if (event.code === 'ArrowDown') {
        this.handleKeyboardInput('DOWN')
      }
    })
  }

  createWalkAnimation () {
    this.createHeroAnimate('LEFT', [4, 5, 6, 7])
    this.createHeroAnimate('RIGHT', [8, 9, 10, 11])
    this.createHeroAnimate('UP', [12, 13, 14, 15])
    this.createHeroAnimate('DOWN', [0, 1, 2, 3])
  }

  createHeroAnimate (keyStr, frameArray) {
    this.anims.create({
      key: keyStr,
      frames: this.anims.generateFrameNumbers(CONST.SPRITE_SHEET_HERO, { frames: frameArray }),
      frameRate: 64,
      repeat: 1
    })
  }

  handleKeyboardInput (key) {
    if (!this.scene) {
      return
    }
    const { map } = this.scene
    if (!map) {
      return
    }

    if (blockInput) {
      return
    }

    let targetX = this.x
    let targetY = this.y

    // 移动时关闭文字提示
    this.hideTip()

    if (key === 'LEFT') {
      targetX -= CONST.BLOCK_SIZE
    } else if (key === 'RIGHT') {
      targetX += CONST.BLOCK_SIZE
    } else if (key === 'UP') {
      targetY -= CONST.BLOCK_SIZE
    } else if (key === 'DOWN') {
      targetY += CONST.BLOCK_SIZE
    }

    // 获取目标位置的对象
    let targetObj = null
    const { objects } = this.scene
    let targetObjIdx
    for (let idx = 0; idx < objects.length; idx += 1) {
      const obj = objects[idx]
      if (Math.abs(obj.x - targetX) < 4 && Math.abs(obj.y - targetY) < 4) {
        // hit the object
        targetObj = obj
        targetObjIdx = idx
      }
    }

    let bHandled = false
    if (targetObj && targetObj.visible) {
      // 地图切换
      if (targetObj.name === 'FloorUp') {
        this.scene.goNextScene()
        return
      }
      if (targetObj.name === 'FloorDown') {
        this.scene.goPrevScene()
        return
      }

      // 处理物品触发的事件
      itemData.items.forEach((item) => {
        if (item.name && item.name === targetObj.name) {
          const { properties } = item
          if (properties && properties.addYellowKey !== undefined) {
            this.scene.game.registry.set('playerYellowKeyCount', this.scene.game.registry.get('playerYellowKeyCount') + properties.addYellowKey)
          }
          if (properties && properties.addBlueKey !== undefined) {
            this.scene.game.registry.set('playerBlueKeyCount', this.scene.game.registry.get('playerBlueKeyCount') + properties.addBlueKey)
          }
          if (properties && properties.addRedKey !== undefined) {
            this.scene.game.registry.set('playerRedKeyCount', this.scene.game.registry.get('playerRedKeyCount') + properties.addRedKey)
          }
          if (properties && properties.addAttack !== undefined) {
            this.scene.game.registry.set('playerAttack', this.scene.game.registry.get('playerAttack') + properties.addAttack)
          }
          if (properties && properties.addDefense !== undefined) {
            this.scene.game.registry.set('playerDefense', this.scene.game.registry.get('playerDefense') + properties.addDefense)
          }
          if (properties && properties.addHp !== undefined) {
            this.scene.game.registry.set('playerHp', this.scene.game.registry.get('playerHp') + properties.addHp)
          }
          this.playSoundItem()

          // 显示文字提示
          const tipStr = item.tip
          if (tipStr) {
            this.showTip(tipStr)
          }

          // 更新怪物损失血量
          this.scene.drawHpLoose()

          bHandled = true
        }
      })
      // 移除物品
      if (targetObjIdx && bHandled) {
        this.recordDeletedObject(targetObj) // 记录被销毁的对象

        objects.splice(targetObjIdx, 1) // 移除引用
        targetObj.destroy() // 销毁对象
      }

      // 处理开门
      if (((targetObj.name === 'RedDoor') || (targetObj.name === 'YellowDoor') || (targetObj.name === 'BlueDoor')) &&
          targetObj.visible) {
        blockInput = true // 开门的时候阻止输入

        const doorType = targetObj.name.replace('Door', '')
        const keyCount = this.scene.game.registry.get(`player${doorType}KeyCount`)
        if (keyCount < 1) {
          return // 没有钥匙
        }
        // 更新钥匙数量
        this.scene.game.registry.set(`player${doorType}KeyCount`, keyCount - 1)
        // 播放开门音乐
        this.playSoundStair()
        // 开门动画处理，动画完毕后自动销毁
        targetObj.play('openDoor')

        this.recordDeletedObject(targetObj) // 记录被销毁的对象

        return
      }
    }

    // 移动
    const tile = map.getTileAtWorldXY(targetX, targetY, true, null, 'base')
    if (tile.index !== CONST.TILE_IDX_ROAD) {
      return // block move
    }
    this.x = targetX
    this.y = targetY
    // play move sound
    this.playSoundStep()

    // 绘制移动的动画
    this.play(key)
  }

  recordDeletedObject (obj) {
    const { mapIndex } = this.scene
    const destroyedObjectDB = this.scene.game.registry.get('destroyedObjectDB')

    if (destroyedObjectDB[mapIndex] === undefined) {
      destroyedObjectDB[mapIndex] = []
    }
    destroyedObjectDB[mapIndex] = destroyedObjectDB[mapIndex]
      .concat({ x: obj.x, y: obj.y, name: obj.name })

    this.scene.game.registry.set('destroyedObjectDB', destroyedObjectDB)
  }

  showTip (str) {
    const tip = this.scene.add.group()

    // 绘制文字背景
    const offsetX = CONST.BLOCK_SIZE / 2
    const offsetY = CONST.BLOCK_SIZE * 5 + CONST.BLOCK_SIZE / 2
    for (let y = 0; y < 2; y += 1) {
      for (let x = 0; x < 17; x += 1) {
        tip.add(this.scene.add.image(offsetX + x * CONST.BLOCK_SIZE, offsetY + y * CONST.BLOCK_SIZE, 'road').setOrigin(0))
      }
    }
    // 绘制背景边界
    const graphics = this.scene.add.graphics()
    graphics.lineStyle(4, 0xcc6600, 1)
    tip.add(graphics.strokeRect(offsetX - 2, offsetY - 2,
      CONST.BLOCK_SIZE * 17 + 4, CONST.BLOCK_SIZE * 2 + 4))

    // 绘制文字，并且居中显示
    const tipText = this.scene.add.text(0, 0, str)
    tipText.setFontFamily('Microsoft YaHei').setFontSize(CONST.BLOCK_SIZE).setColor('#ffffff')
    tipText.x = (this.scene.game.config.width - tipText.getBounds().width) / 2
    tipText.y = (this.scene.game.config.height - tipText.getBounds().height) / 2
    tip.add(tipText)

    // 1秒后消失
    this.scene.time.addEvent({
      delay: 500, callback: this.hideTip, callbackScope: this, loop: false
    })

    this.tip = tip

    // 显示提示时就不要再移动了
    blockInput = true
  }

  hideTip () {
    if (this.tip) {
      this.tip.destroy(true)
      this.tip = null
    }

    blockInput = false
  }

  playSoundStep () {
    this.playSound('step')
  }

  playSoundItem () {
    this.playSound('item1')
  }

  playSoundStair () {
    this.playSound('stair')
  }

  playSound (name) {
    const snd = this.scene.sound.add(name)
    if (!snd.isPlaying) {
      snd.play()
    }
  }
}
